Functions
Classic Functions
func greeting(for person: String) -> String {
"Hello, " + person + "!"
}
print(greeting(for: "Dave"))
// Prints "Hello, Dave!"
func anotherGreeting(for person: String) -> String {
return "Hello, " + person + "!"
}
print(anotherGreeting(for: "Dave"))
// Prints "Hello, Dave!"
func greet(person: String) -> String {
let greeting = "Hello, " + person + "!"
return greeting
}
print(greet("Dave"))
// Prints "Hello, Dave!"
func greet(person: String) {
print("Hello, \(person)!")
}
greet(person: "Dave")
// Prints "Hello, Dave!"
Mutability: inout
-
Function parameters are constants by default. Trying to change the value of a function parameter from within the body of that function results in a compile-time error.
-
If you want a function to modify a parameter’s value, and you want those changes to persist after the function call has ended, define that parameter as an in-out parameter instead.
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// Prints "someInt is now 107, and anotherInt is now 3"
-
Note :
-
In-out parameters can’t have default values, and variadic parameters can’t be marked as
inout.
-
Multiple returns
func minMax(array: [Int]) -> (min: Int, max: Int) {
var currentMin = array[0]
var currentMax = array[0]
for value in array[1..<array.count] {
if value < currentMin {
currentMin = value
} else if value > currentMax {
currentMax = value
}
}
return (currentMin, currentMax)
}
let bounds = minMax(array: [8, -6, 2, 109, 3, 71])
print("min is \(bounds.min) and max is \(bounds.max)")
// Prints "min is -6 and max is 109"
-
Can be optional:
func minMax(array: [Int]) -> (min: Int, max: Int)? { if array.isEmpty { return nil } var currentMin = array[0] var currentMax = array[0] for value in array[1..<array.count] { if value < currentMin { currentMin = value } else if value > currentMax { currentMax = value } } return (currentMin, currentMax) }
Ignoring the return
-
The return value of a function can be ignored when it’s called:
func printAndCount(string: String) -> Int {
print(string)
return string.count
}
let _ = printAndCount(string: "Caio")
Parameters
Order
-
Using
_in a function allows positional arguments, as inwhatsUp('jeff', emoji: 'foguinho').
Default
func someFunction(parameterWithoutDefault: Int, parameterWithDefault: Int = 12) {
// If you omit the second argument when calling this function, then
// the value of parameterWithDefault is 12 inside the function body.
}
someFunction(parameterWithoutDefault: 3, parameterWithDefault: 6) // parameterWithDefault is 6
someFunction(parameterWithoutDefault: 4) // parameterWithDefault is 12
Labels
-
If a parameter has an argument label, the argument must be labeled when you call the function.
func greeting(for person: String) -> String {
"Hello, " + person + "!"
}
print(greeting(for: "Dave"))
// Prints "Hello, Dave!"
func anotherGreeting(for person: String) -> String {
return "Hello, " + person + "!"
}
print(anotherGreeting(for: "Dave"))
// Prints "Hello, Dave!"
-
Omitting the label:
func someFunction(_ firstParameterName: Int, secondParameterName: Int) { // In the function body, firstParameterName and secondParameterName // refer to the argument values for the first and second parameters. } someFunction(1, secondParameterName: 2)
Variadic
func arithmeticMean(_ numbers: Double...) -> Double {
var total: Double = 0
for number in numbers {
total += number
}
return total / Double(numbers.count)
}
arithmeticMean(1, 2, 3, 4, 5)
// returns 3.0, which is the arithmetic mean of these five numbers
arithmeticMean(3, 8.25, 18.75)
// returns 10.0, which is the arithmetic mean of these three numbers
Function as Arguments / Function Types
-
Functions are 'first-class objects'.
-
"
(Int, Int) -> Int":func addTwoInts(_ a: Int, _ b: Int) -> Int { return a + b }let anotherMathFunction = addTwoInts // anotherMathFunction is inferred to be of type (Int, Int) -> Int -
"
() -> Void":func printHelloWorld() { print("hello, world") } -
Ex1 :
var mathFunction: (Int, Int) -> Int = addTwoInts print("Result: \(mathFunction(2, 3))") // Prints "Result: 5"func printMathResult(_ mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) { print("Result: \(mathFunction(a, b))") } printMathResult(addTwoInts, 3, 5) // Prints "Result: 8" -
Ex2 :
func stepForward(_ input: Int) -> Int { return input + 1 } func stepBackward(_ input: Int) -> Int { return input - 1 } func chooseStepFunction(backward: Bool) -> (Int) -> Int { return backward ? stepBackward : stepForward } var currentValue = 3 let moveNearerToZero = chooseStepFunction(backward: currentValue > 0) // moveNearerToZero now refers to the stepBackward() function -
Ex3 :
func backward(_ s1: String, _ s2: String) -> Bool { return s1 > s2 } var reversedNames = names.sorted(by: backward) // reversedNames is equal to ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
Nested Functions
-
Nested functions are hidden from the outside world by default.
func chooseStepFunction(backward: Bool) -> (Int) -> Int {
func stepForward(input: Int) -> Int { return input + 1 }
func stepBackward(input: Int) -> Int { return input - 1 }
return backward ? stepBackward : stepForward
}
-
Can be returned as a parameter to be accessed outside the function.
Closures
Ways to Write
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2
})
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 } )
reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
reversedNames = names.sorted(by: { $0 > $1 } ) // shorthanded
reversedNames = names.sorted(by: >) // Using the default implementation of the `>` symbol
When the Closure is Called
-
An autoclosure is a closure that’s automatically created to wrap an expression that’s being passed as an argument to a function.
-
It doesn’t take any arguments, and when it’s called, it returns the value of the expression that’s wrapped inside of it.
-
An autoclosure lets you delay evaluation because the code inside isn’t run until you call the closure.
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count)
// Prints "5"
let customerProvider = { customersInLine.remove(at: 0) }
print(customersInLine.count)
// Prints "5"
print("Now serving \(customerProvider())!")
// Prints "Now serving Chris!"
print(customersInLine.count)
// Prints "4"
// customersInLine is ["Alex", "Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: () -> String) {
print("Now serving \(customerProvider())!")
}
serve(customer: { customersInLine.remove(at: 0) } )
// Prints "Now serving Alex!"
Shorthand
reversedNames = names.sorted(by: { $0 > $1 } )
-
Here,
$0and$1refer to the closure’s first and secondStringarguments. Because$1is the shorthand argument with the highest number, the closure is understood to take two arguments. Because thesorted(by:)function here expects a closure whose arguments are both strings, the shorthand arguments$0and$1are both of typeString.
Ways to Call (Trailing Closures)
-
You write a trailing closure after the function call’s parentheses, even though the trailing closure is still an argument to the function.
-
When you use the trailing closure syntax, you don’t write the argument label for the first closure as part of the function call.
-
A function call can include multiple trailing closures; however, the first few examples below use a single trailing closure.
reversedNames = names.sorted(by: { $0 > $1 } )
reversedNames = names.sorted() { $0 > $1 }
-
If a closure expression is provided as the function’s or method’s only argument and you provide that expression as a trailing closure, you don’t need to write a pair of parentheses
()after the function or method’s name when you call the function:reversedNames = names.sorted { $0 > $1 } -
Ex1 :
let digitNames = [ 0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four", 5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine" ] let numbers = [16, 58, 510] let strings = numbers.map { (number) -> String in var number = number var output = "" repeat { output = digitNames[number % 10]! + output number /= 10 } while number > 0 return output } // strings is inferred to be of type [String] // its value is ["OneSix", "FiveEight", "FiveOneZero"] -
Ex2 (multiple closures):
func loadPicture(from server: Server, completion: (Picture) -> Void, onFailure: () -> Void) { if let picture = download("photo.jpg", from: server) { completion(picture) } else { onFailure() } }loadPicture(from: someServer) { picture in someView.currentPicture = picture } onFailure: { print("Couldn't download the next picture.") }